<script setup>
import { computed } from "vue";
import { getWeekName } from "../util";
import DayItem from "./DayItem.vue";

const props = defineProps(["year", "month", "firstDayOfWeek", "selectedDate"]);
const emit = defineEmits(["change"]);

function offsetDate(date, days) {
  return new Date(date.getTime() + days * 24 * 3600 * 1000);
}

function isSameMonth(a, b) {
  if (a.getFullYear() != b.getFullYear()) {
    return false;
  }
  if (a.getMonth() != b.getMonth()) {
    return false;
  }
  return true;
}

function isSameDay(a, b) {
  if (!isSameMonth(a, b)) {
    return false;
  }
  if (a.getDate() != b.getDate()) {
    return false;
  }
  return true;
}

const today = new Date();

const year = computed(() => props.year === undefined ? today.getFullYear() : props.year);
const month = computed(() => props.month === undefined ? today.getMonth() : props.month);
const selectedDate = computed(() => props.selectedDate === undefined ? today : props.selectedDate);
const firstDayOfWeek = computed(() => props.firstDayOfWeek || 1);
const lastDayOfWeek = computed(() => (firstDayOfWeek.value + 6) % 7);

const firstDayOfMonth = computed(() => new Date(year.value, month.value, 1));
const lastDayOfMonth = computed(() => new Date(year.value, month.value + 1, 0));
const firstDay = computed(() => {
  const difference = (firstDayOfMonth.value.getDay() - firstDayOfWeek.value + 7) % 7;
  return offsetDate(firstDayOfMonth.value, -difference);
});
const lastDay = computed(() => {
  const difference = (lastDayOfWeek.value - lastDayOfMonth.value.getDay() + 7) % 7;
  return offsetDate(lastDayOfMonth.value, difference);
});

const titles = computed(() => {
  const result = [];
  for (let i = 0; i < 7; i++) {
    result[i] = getWeekName((firstDayOfWeek.value + i) % 7);
  }
  return result;
});

const weeks = computed(() => {
  let week = [];
  const result = [];
  for (let i = firstDay.value; i.getTime() <= lastDay.value.getTime(); i = offsetDate(i, 1)) {
    week.push({
      type: "day",
      sameMonth: isSameMonth(i, today),
      date: i
    });
    if (i.getDay() == lastDayOfWeek.value) {
      result.push(week);
      week = [];
    }
  }
  if (week.length != 0) {
    result.push(week);
  }
  return result;
});
</script>

<template>
  <table class="calendar">
    <thead>
      <th class="body-large" :key="index" v-for="(title, index) in titles">{{ title }}</th>
    </thead>
    <tbody>
      <tr :key="index" v-for="(week, index) in weeks">
        <template v-for="day in week">
          <DayItem :key="day.date.getTime()" v-if="day.type == 'day'" :today="isSameDay(day.date, today) || undefined"
            :outsideMonth="day.date.getMonth() != month || undefined"
            :selected="isSameDay(day.date, selectedDate || today) || undefined" @click="$emit('change', day.date)">
            {{ day.date.getDate() }}
          </DayItem>
        </template>
      </tr>
    </tbody>
  </table>
</template>

<style scoped>
table.calendar * {
  padding: 0;
  margin: 0;
}

table.calendar {
  display: inline-block;
}

table.calendar>thead {
  display: flex;
  gap: 8px;
  padding-bottom: 8px;
}

table.calendar>tbody>tr {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
}

table.calendar>tbody>tr:last-child {
  margin-bottom: 0;
}

table.calendar>tbody {
  display: block;
}

table.calendar>thead>th {
  font-weight: normal;
  color: var(--md-sys-color-on-surface);
  user-select: none;
}

table.calendar>thead>th,
table.calendar>tbody>tr>td {
  height: 40px;
  width: 40px;
  box-sizing: border-box;
  display: inline-block;
  line-height: 40px;
}
</style>